
#include <BRender>
#include <BMaterial>
#include <BScene>
#include "member_BCamera.h"
#include "member_BLight.h"
#include "member_BNode.h"
#include "member_BShape.h"
#include "member_BControl.h"
#include "member_BScene.h"

using namespace BWE;

member_BCamera::member_BCamera(BCamera* camera)
{
	boss = camera;
	ortho = false;
	up.set(0, 0, 1);
	left = -1;
	right = 1;
	top = 1;
	bottom = -1;
	fovy = 36.0;
	aspect = 1.0;
	near = 1;
	far = 1000;
	culling = true;
	scene = 0;
}
member_BCamera::~member_BCamera()
{

}

void member_BCamera::adjust()
{
	if (ortho)
	{
		double height = top - bottom;
		double width = height * aspect;
		double mid = (left + right) * 0.5;
		left = mid - width * 0.5;
		right = mid + width  * 0.5;
		return;
	}
	if (fovy > 0.0)
	{
		double hfovy = fovy * 0.5  / 180.0 * PI;
		double hh = near * tan(hfovy);
		double hw = hh * aspect;
		top = hh;
		bottom = -hh;
		left = -hw;
		right = hw;
	}
}

void member_BCamera::frame(BScene* scene, BControl* control)
{
	this->scene = scene;

	pixmap->update();

	if (pixmap->_fbo)
	{
		BArray<BShapeHolder>& shapes = scene_member(scene)->shapes;
		BArray<BNodeHolder>& nodes = scene_member(scene)->nodes;
		BArray<BLightHolder>& lights = scene_member(scene)->lights;

		BRender render(boss);

		glClearDepth(1);
		glClear(GL_DEPTH_BUFFER_BIT);

		glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
		glClear(GL_COLOR_BUFFER_BIT);

		for (int i = 0; i < shapes.size(); i++)
		{
			BShape* shape = shapes[i];
			if (shape->name() == "debug")
				int a = 0;
			draw(shape, render);
		}

		for (int i = 0; i < nodes.size(); i++)
		{
			BNode* node = nodes[i];
			draw(node, render);
		}

		renderControl(control, render);

		if (photoImage)
		{
			BSize size = pixmap->size();
			Format format = photoImage->format();
			photoImage->setSize(size);
			BByteArray* pixels = photoImage->pixels();
			if (format == Format_RGBA && pixels)
			{
				glReadPixels(0, 0, size.width(), size.height(), GL_RGBA, GL_UNSIGNED_BYTE, pixels->data());
			}
			if (format == Format_RGB && pixels)
			{
				glReadPixels(0, 0, size.width(), size.height(), GL_RGB, GL_UNSIGNED_BYTE, pixels->data());
			}
			photoImage->dirty();
		}
	}

	this->scene = 0;
}
void member_BCamera::prepareLights(const BSpace& space)
{
	BArray<BLightHolder>& lights = scene_member(scene)->lights;

	BLight*	light_array[8];
	for (int i = 0; i < 8; i++)
	{
		light_array[i] = lights(i);
	}
	for (int i = 0; i < 8; i++)
	{
		BLight* light = light_array[i];
		int index = GL_LIGHT0 + i;
		if (!light)
		{
			if (i > 0)
				glDisable(index);
			continue;
		}
		glEnable(index);

		glLightfv(index, GL_AMBIENT, (const GLfloat*)&light_member(light)->ambientColor);
		glLightfv(index, GL_DIFFUSE, (const GLfloat*)&light_member(light)->diffuseColor);
		glLightfv(index, GL_SPECULAR, (const GLfloat*)&light_member(light)->specularColor);
		glLightf(index, GL_CONSTANT_ATTENUATION, 1.0f);
		glLightf(index, GL_LINEAR_ATTENUATION, light->attenuation());
		glLightf(index, GL_QUADRATIC_ATTENUATION, light->attenuation2());

		BVector position = light->position();
		if (light_member(light)->type == BLight::Type_Point)
		{
			GLfloat pos[4] = { position.x(), position.y(), position.z(), 1.0f };
			glLightfv(index, GL_POSITION, pos);
			continue;
		}
		if (light_member(light)->type == BLight::Type_Spot)
		{
			BVector direction = light->direction();
			GLfloat pos[4] = { position.x(), position.y(), position.z(), 1.0f };
			GLfloat dir[3] = { direction.x(), direction.y(), direction.z() };
			glLightfv(index, GL_POSITION, pos);
			glLightfv(index, GL_SPOT_DIRECTION, dir);
			glLightf(index, GL_SPOT_CUTOFF, light->cutoff());
			glLightf(index, GL_SPOT_EXPONENT, light->exponent());
			continue;
		}
		if (light_member(light)->type == BLight::Type_Direct)
		{
			GLfloat pos[4] = { position.x(), position.y(), position.z(), 0.0f };
			glLightfv(index, GL_POSITION, pos);
			continue;
		}
	}
}
void member_BCamera::draw(BNode* node, BRender& render)
{
	if (node->visible() == false)
		return;

	glPushMatrix();
	glPushAttrib(GL_ALL_ATTRIB_BITS);

	const BState* state = node->state();
	render.setState(state);

	if (render.lighting())
	{
		prepareLights(node->space());
	}

	BMatrix matrix = node->matrix();
	glMultMatrixf((const GLfloat*)&matrix);

	const BVector scale = matrix.scale();
	glScalef(scale.x(), scale.y(), scale.z());

	node_member(node)->render(render);

	for (int i = 0; i < node->shapeCount(); i++)
	{
		BShape* shape = node->shape(i);
		if (shape->name() == "debug")
			int a = 0;
		if (!shape->visible())
			continue;
		draw(shape, render);

	}

	glPopAttrib();
	glPopMatrix();
}
void member_BCamera::draw(BShape* shape, BRender& render)
{
	glPushMatrix();
	glPushAttrib(GL_ALL_ATTRIB_BITS);

	if (render.lighting())
	{
		prepareLights(shape->space());
		const BMaterial* material = shape_member(shape)->material;
		if (material)
		{
			render.setMaterial(material);
			glDisable(GL_COLOR_MATERIAL);
		}
		else
		{
			glEnable(GL_COLOR_MATERIAL);
		}
	}
	else
	{
		glDisable(GL_COLOR_MATERIAL);
	}

	const BState* state = shape_member(shape)->state;
	render.setState(state);

	const BMatrix& matrix = shape_member(shape)->matrix;
	glMultMatrixf((const GLfloat*)&matrix);

	const BProgram* program = shape_member(shape)->program;
	render.setProgram(program);

	const BColor& color = shape_member(shape)->color;
	render.setColor(color);

	if (shape_member(shape)->textures.size())
	{
		glEnable(GL_TEXTURE_2D);
		BArray<BTextureHolder>& textures = shape_member(shape)->textures;
		for (int i = 0; i < textures.size(); i++)
		{
			glActiveTexture(GL_TEXTURE0 + i);
			if (textures[i])
			{
				textures[i]->bind();
			}
			else
			{
				glBindTexture(GL_TEXTURE_2D, 0);
			}
		}
		glActiveTexture(GL_TEXTURE0);
	}
	else
	{
		glDisable(GL_TEXTURE_2D);
	}

	shape_member(shape)->render(render);

	if (program)
		render.setProgram(0);

	glPopAttrib();
	glPopMatrix();
}

void member_BCamera::renderControl(BControl* control, BRender& render)
{
	if (!control || !control->able())
		return;
	control_member(control)->render(render);
	for (int i = 0; i < control_member(control)->slaves.size(); i++)
	{
		BControl* slave = control_member(control)->slaves[i];
		renderControl(slave, render);
	}
}

